﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Diagnostics;

namespace Mtidata0225
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        string str = " ";
       // byte[] buf = new byte[5000];    //存放最终的数据考虑用动态的来实现？？
        List<byte> buf=new List<byte>();
        int l = 0;
        int len = 0;                   //用在数据图形绘制函数中
        bool serialportcom = false;    //串口初始值
        bool baudrate = false;        //波特率设置
        bool speed = false;
        bool bgndispflag = false;    //开始显示标志位
        bool setflag = false;      //对应的按键为“确认”
        bool sendflag = false;     //用在数据发送区，确认发送指令
        string dispstyle = "十六进制";    //默认16进制显示
      //  string dispstyle = "";
        uint checksum = 0;
        uint DatareceivedSum = 0;
        byte MID = 0;
        byte datalength = 0;            //数据帧初始长度为0
        uint sum = 0;
        OlddataDisp postProcessed = new OlddataDisp();
        List<double> mylist=new List<double>();   //容量可变的容器

        /// <summary>
        /// 串口设置程序
        /// </summary>
        /// <param name="sender"></param> 
        /// <param name="e"></param>
        private void confirmbtn_Click(object sender, EventArgs e)
        {
            if (portnamelbe.Text != "")
                serialportcom = true;
            else
                serialportcom = false;

            if (baudratelble.Text != "")
                baudrate = true;
            else
                baudrate = false;

            if (speedcombox.Text != "")
                speed = true;
            else
                speed = false;

            if (baudrate == false || serialportcom == false||speed==false)
                MessageBox.Show("请先对串口进行设置，并按确认键", "系统提示");
            else                  //true at the same time 
            {
                setflag = true;
                if (serialPort1.IsOpen == false)
                {
                    serialPort1.PortName = Convert.ToString(portnamelbe.Text);
                    serialPort1.BaudRate = Convert.ToInt32(baudratelble.Text);
                    serialPort1.Open();        //打开串口
                }
               // drawaxis();
            }
        }

        /// <summary>
        /// 设置显示格式以及开始显示
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bgndispbtn_Click(object sender, EventArgs e)
        {
            if (setflag == false)
            {
                MessageBox.Show("请先对串口进行设置,并按确认键", "系统提示");
                return;
            }
           // bgndispflag = true;
          //  serialPort1.Open();
        }

        /// <summary>
        /// 指令发送程序
        /// 为什么发送的时候自动添加了0x0a了呢？
        /// 为什么不能实现指令的发送呢？？
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void sendbtn_Click(object sender, EventArgs e)
        {                        //为什么不能给MTI发送指令呢????
            if (setflag == false)     //先检查串口的基本设置是否正确
            {
                MessageBox.Show("请先对串口进行设置,并按确认键", "系统提示");
                return;
            }

            if (richTextBox2.Text == "")
            {
                MessageBox.Show("请输入发送内容", "系统提示");
                return;
            }
            sendflag = true;
            if (serialPort1.IsOpen == false)
                serialPort1.Open();
           // byte[] data = Encoding.Unicode.GetBytes(richTextBox2.Text);
           // string sendstr = Convert.ToBase64String(data);  //解码时也必须是基于Base64
            //serialPort1.WriteLine(richTextBox2.Text);   //发送内容   //否则的话会出错
            serialPort1.Write(richTextBox2.Text);
            //MessageBox.Show("数据发送成功！", "系统提示");

        }

        /// <summary>
        /// 显示清空按键，将所有的显示内容清空
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void clcbtn_Click(object sender, EventArgs e)
        {
            str = "";
            richTextBox1.Text = "";
            l = 0;
        }

        /// <summary>
        /// 数据接收触发串口程序,全放在里边造成运算时间过长
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {    //为什么重新上电后才能使用？？(有可能是原先的MTmanager继续占用端口)
             int n = serialPort1.BytesToRead;//读取当前有多少数据可读,串口中传送的数据都是16进制的
             byte[] buffer = new byte[n];    //新建内容
             DatareceivedSum += Convert.ToUInt32(n);
             serialPort1.Read(buffer, 0, n);   //读取缓冲区，读的同时又清空了串口缓存
             buf.AddRange(buffer);            //接收到的数据最终都是放到buf中进行处理了
        }


        /// <summary>
        /// IEEE754标准，将4字节的数据转换成单精度浮点型数据
        /// </summary>
        public  void caculate(byte[] data)    //将16进制按照IEEE754标准进行转换
        {    //public,static区别对于调用其它的很重要
            string conStr = "Data Source=.;Initial Catalog=用户登记;Integrated Security=True";
            SqlConnection connection = new SqlConnection(conStr);
            sbyte signalbit = 1;    //默认符号位，默认时候为正数
            byte zhishu = 0;          //指数上的值
            double M_value = 0;         //后23位数据的值
            double acccurvedata=0;      //存放z向加速度的值
            UInt32 datatmp = 0;
            UInt32 datatmp1 = 0;
            double[] finalvalue = new double[14];       //最后的输出值
            connection.Open();
            for (uint i = 0; i < data.Length-3; i += 4) //由于数据区最后三位为TS，status
            {                                       //计算的时候不用，所以不需要。
                if ((data[i] & 0x80) == 0x80)
                    signalbit = -1;         //第一位为1则表示为负数
                else
                    signalbit = 1;         //otherwise positive
                datatmp = Convert.ToUInt32(data[i] & 0x7f);      //shift left
                datatmp <<= 8;
                datatmp = datatmp | data[i + 1];
                datatmp = datatmp >> 7;
                zhishu = Convert.ToByte(datatmp);     //求出指数部分大小

                datatmp = 0;
                datatmp = Convert.ToUInt32(data[i + 1] & 0x7f);
                datatmp <<= 16;
                datatmp |= data[i + 3];
                datatmp1 = data[i + 2];
                datatmp1 <<= 8;
                datatmp |= datatmp1;
                datatmp1 = datatmp;
                UInt32 shift = 0x0001;
                for (uint j = 0; j < 23; j++)    //4字节数据只选择23位进行使用
                {
                    if ((datatmp & shift) != 0)
                    {
                        M_value += (1 / Math.Pow(2, 23 - j));
                        shift <<= 1;
                    }
                    else
                    {
                        shift <<= 1;
                        continue;
                    }
                }
                //finalvalue[i / 4] =Convert.ToDouble(( (1 + M_value) //所有数据保留小数点后8位
                //    * Math.Pow(2, zhishu - 127) * signalbit).ToString("#.000000000000"));
                finalvalue[i / 4] = Math.Round(((1 + M_value) //所有数据保留小数点后8位
                    * Math.Pow(2, zhishu - 127) * signalbit),2);
                M_value = 0;
                datatmp = 0;
                datatmp1 = 0;
                if (finalvalue[2] != 0)   //这一次数据全采集完了才显示,11 before
                {
                    datadisp(finalvalue);      //没必要实时显示数值吧？这样占用的时间太多
                    //finalvalue[12] = Math.Round(Math.Acos(finalvalue[2] / 10) * 57.3, 2);
                    //finalvalue[12] = 1;//这样造成的问题是能测静态的角度，但不能测量动态的，因为动态过程中
                    //分子的值很有可能大于分母，这就造成数学上的错误，会提醒出错
                    //finalvalue[13] = Math.Round(Math.Asin(finalvalue[0] / 9.8) * 57.3, 2);
                    try
                    {
                        //connection.Open();
                        //string sqlcmd = string.Format("INSERT INTO 惯导数据(accx,accy,accz,gyrx,gyry,gyrz,magx,magy,magz,roll,pitch,yaw,z轴角度,x轴角度)" +
                        //"VALUES('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}')", finalvalue[0], finalvalue[1], finalvalue[2], finalvalue[3], finalvalue[4], finalvalue[5], finalvalue[6], finalvalue[7],
                        //finalvalue[8], finalvalue[9], finalvalue[10], finalvalue[11], finalvalue[12], finalvalue[13]);
                        string sqlcmd = string.Format("INSERT INTO 惯导数据(测试速度,accx,accy,accz,gyrx,gyry,gyrz,magx,magy,magz,roll,pitch,yaw)" +
                        "VALUES('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}',{10})",Convert.ToDouble(speedcombox.Text), finalvalue[0], finalvalue[1], finalvalue[2], finalvalue[3], finalvalue[4], finalvalue[5], finalvalue[6], finalvalue[7],
                        finalvalue[8], finalvalue[9], finalvalue[10], finalvalue[11]);
                       // string sqlcmd = string.Format("INSERT INTO 惯导数据(accx,accy,accz,gyrx,gyry,gyrz,magx,magy,magz)" +
                       //"VALUES('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}')", finalvalue[0], finalvalue[1], finalvalue[2], finalvalue[3], finalvalue[4], finalvalue[5], finalvalue[6], finalvalue[7],
                       //finalvalue[8]);
                        SqlCommand sqlCommand = new SqlCommand(sqlcmd, connection);
                        sqlCommand.ExecuteNonQuery();
                    }
                    catch (System.Exception ex)
                    {
                        MessageBox.Show(ex.ToString());
                    }
                    finally
                    {
                       // connection.Close();
                    }
                    //connection.Open();
                    ////acccurvedata = finalvalue[2];    //z向加速度
                    ////drawcurves(acccurvedata);     //调用绘制曲线函数
                    //string sqlcmd = string.Format("INSERT INTO 惯导数据(accx,accy,accz,gyrx,gyry,gyrz,magx,magy,magz,roll,pitch,yaw,z轴角度,x轴角度)" +
                    //"VALUES('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}')", finalvalue[0], finalvalue[1], finalvalue[2], finalvalue[3], finalvalue[4], finalvalue[5], finalvalue[6], finalvalue[7],
                    //finalvalue[8], finalvalue[9], finalvalue[10], finalvalue[11],finalvalue[12],finalvalue[13]);
                    //SqlCommand sqlCommand = new SqlCommand(sqlcmd, connection);
                    //sqlCommand.ExecuteNonQuery();
                    //connection.Close(); 
                    finalvalue[2]=0;             //每一次只保留一下***  
                    break;          //后来新添加的***
                }
            }
            connection.Close();
        }

        /// <summary>
        /// 跨线程调用不是很了解，如何更好的解决数据显示的问题？？难道真的要一句一句写？？太麻烦了？
        /// </summary>
        /// <param name="finalvalue"></param>
        public  void datadisp(double[] finalvalue)
        {
            this.xacctxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.xacctxt.Text = Convert.ToString(finalvalue[0]);
                    //  this.xacctxt.Text = finalvalue[0].ToString("#.00000");
                  }

                )
            );
            this.yacctxt.Invoke    
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.yacctxt.Text = Convert.ToString(finalvalue[1]);
                  }

                )
            );
            this.zacctxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.zacctxt.Text = Convert.ToString(finalvalue[2]);
                  }

                )
            );
            this.xgrytxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.xgrytxt.Text = Convert.ToString(finalvalue[3]);
                  }

                )
            );
            this.ygrytxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.ygrytxt.Text = Convert.ToString(finalvalue[4]);
                  }

                )
            );
            this.zgrytxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.zgrytxt.Text = Convert.ToString(finalvalue[5]);
                  }

                )
            );
            this.xmagtxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.xmagtxt.Text = Convert.ToString(finalvalue[6]);
                  }

                )
            );
            this.ymagtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.ymagtxt.Text = Convert.ToString(finalvalue[7]);
                  }

                )
            );
            this.zmagtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.zmagtxt.Text = Convert.ToString(finalvalue[8]);
                  }

                )
            );
            this.rolltxt.Invoke    //所有显示数据都要用这种方法吗？？
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.rolltxt.Text = Convert.ToString(finalvalue[9]);
                  }

                )
            );
            this.pitchtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.pitchtxt.Text = Convert.ToString(finalvalue[10]);
                  }

                )
            );
            this.yawtxt.Invoke
            (
                new MethodInvoker     //线程切换
                (
                  delegate
                  {
                      this.yawtxt.Text = Convert.ToString(finalvalue[11]);
                  }

                )
            );
        }        

        #region         //保存采集过来的数据
        private void savedatabtn_Click(object sender, EventArgs e)
        {
            serialPort1.Close();
            string fname = "";
            sfd.Filter = "文本文件(*.txt)|*.txt|C#文件(*.cs)|*.cs|所有文件|*.*|word文档(*.doc)|*.doc";
            sfd.FilterIndex = 2;
            sfd.RestoreDirectory = true;
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                fname = sfd.FileName;
                savefile(fname);
            }
        }

        protected void savefile(string filename)
        {      
            try
            {
                Stream stream = File.OpenWrite(filename);
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    writer.Write(richTextBox1.Text);
                }
            }
            catch (IOException ex)
            {
                MessageBox.Show(ex.Message, "Simple Editor", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
        #endregion       

        private void Form1_Load_1(object sender, EventArgs e)
        {
            //intial();
            uint i = 0;          //新建一个专门图像显示的线程？？3-3
            string[] PortNames = System.IO.Ports.SerialPort.GetPortNames(); //检测串口
            for (i = 0; i < PortNames.Length; i++)
            {
                this.portnamelbe.Items.Add(PortNames[i]);
            }
        }

//         public void intial()
//         {
//             label1.Text = "t/s";
//             zdirlab.Text = "m/s2";
//             ydirlab.Text = "0";
//         }

        private void Datadisplay(List<byte> buffer)
        {
             char[] str1 = { 'a', 'b', 'c', 'd', 'e', 'f' };
             int gao = 0, di = 0;
             if (decirbtn.Checked == true)  //判断使用哪种方式进行显示
                 dispstyle = decirbtn.Text;
             if (hexrbtn.Checked == true)
                 dispstyle = hexrbtn.Text;
             switch(dispstyle)             //数据显示区显示的数据格式
             {
                 case "十六进制":
                 {
                     str = "";
                     for (int j = 0; j < buffer.Count; j++)  //16进制显示
                     {
                         di = buffer[j] & 0x0f;
                         gao = buffer[j] & 0xf0;
                         gao = gao >> 4;
                         if (gao >= 0 & gao <= 9)
                             str += Convert.ToString(gao);
                         else
                             str += Convert.ToString(str1[gao - 10]);
                         if (di >= 0 & di <= 9)
                             str += Convert.ToString(di);
                         else
                             str += Convert.ToString(str1[di - 10]);
                         str += " ";
                     }
                 }break;
                 case"十进制":
                 {
                     str = "";
                     for (int j = 0; j < buffer.Count; j++)
                     {
                         //str += Convert.ToChar(buffer[j]);
                         str += buffer[j];
                         str += " ";
                     }
                 }break;
                 default:break;
             }
             this.richTextBox1.Invoke
             (
                 new MethodInvoker     //线程切换
                 (
                   delegate
                   {
                       this.richTextBox1.AppendText(str);
                   }

                 )
             );
        }

        private void timer2_Tick(object sender, EventArgs e)
        {
             timer2.Enabled = false;
             List<byte> temp_buf = new List<byte>();
             byte[] databuffer=new byte[39];//51 before
             temp_buf.AddRange(buf);
             buf.Clear();          //及时清空，存储其他数据
             Datadisplay(temp_buf);        //数据显示
             if (temp_buf.Count >= 120)     //保证两帧完整数据,120before
             {
                 for (int i = 0; i < (temp_buf.Count-3); i++)//应该放在最后数据采集完后再处理……
                 {
                     if (temp_buf[i] != 0xfa)   //一直在检索，直到找到报头
                         continue;
                     else                       //找到报头的话
                     {
                         if (temp_buf[i + 1] != 0xff)    //起始位
                             continue;      //此处是返回到哪一级,返回到for那一级
                         else                //返回到上一级循环
                         {
                             i += 2;    //下标越界
                             MID = temp_buf[i];
                             if (MID != 0x32)
                                 continue;
                             i++;
                             datalength = temp_buf[i];
                             if (datalength !=databuffer.Length)
                                 continue;
                         }
                     }
                     for (int m = i + 1; (m < i + 1 + datalength) && (m < temp_buf.Count); m++)
                         databuffer[m - i - 1] = temp_buf[m];
                     for (uint k = 0; k < databuffer.Length; k++)      //复制进去
                         sum += databuffer[k];
                     sum += 0xff;
                     sum += MID;
                     i += (datalength + 1);   //下标运算很麻烦，很容易出错*****
                     if (i >= temp_buf.Count)
                         break;
                     checksum = temp_buf[i];
                     sum += checksum;
                     sum += datalength;
                     if ((sum & 0x00ff) == 0)    //校验和
                     {
                         caculate(databuffer);
                         sum = 0;
                         temp_buf.RemoveRange(0,i+1);   //这一句的位置很重要
                         i = -1;    //一帧一帧处理
                     }
                     else
                     {
                         sum = 0;
                         temp_buf.RemoveRange(0, i+1);   //这一句的位置很重要
                         i = -1;    //一帧一帧处理
                         continue;
                     }
                 }
                 temp_buf.Clear();     //前面的运算进行完后清空内存   
             }

             timer2.Enabled = true;
        }

        private void datadispbtn_Click(object sender, EventArgs e)
        {
            Stopwatch tm = new Stopwatch();
            int i = 0;
            List<double> postData = new List<double>();
            string conStr = "Data Source=.;Initial Catalog=用户登记;Integrated Security=True";
            SqlConnection connection = new SqlConnection(conStr);
            string sqlcmmd = "SELECT accx,accy,accz FROM 惯导数据";
            string sqlcmmdCount = "SELECT COUNT(*) FROM 惯导数据";
            if (postProcessed == null || postProcessed.IsDisposed)
            {
                postProcessed = new OlddataDisp();   //先声明后定义
            }
            //postProcessed.Show();                  //如何将采集到的值在子窗口中显示
            try
            {
                tm.Start();
                connection.Open();
                SqlCommand cmdCount = new SqlCommand(sqlcmmdCount, connection);
                i = Convert.ToInt16(cmdCount.ExecuteScalar());           //返回数据库中记录数
                if (i == 0)
                {
                    MessageBox.Show("数据库中无任何数据记录");
                }
                else
                {
                    postProcessed.Show(); 
                    SqlCommand cmd = new SqlCommand(sqlcmmd, connection);
                    SqlDataReader dataReader = cmd.ExecuteReader();//测一下整个数据库读取数据时间
                    while (dataReader.Read())         //读一行数据显示一个点,逐行读取,读完自动换一行
                    {
                        //for (i = 2; i < 12; i++)   //Datetime 中Null类型不能转换成其他类型
                        //{
                        //    postData.Add(Convert.ToDouble(dataReader[i]));   //如何确定读取的是哪一行数据
                        //}
                        for (i = 0; i < 3; i++)
                            postData.Add(Convert.ToDouble(dataReader[i]));
                    }
                    tm.Stop();
                    MessageBox.Show(tm.ElapsedMilliseconds.ToString());
                    postProcessed.chart1.Invoke
                    (
                        new MethodInvoker
                            (
                                delegate
                                {
                                    //while (postData.Count > 0)
                                    for (i = 0; i < postData.Count; )  //如何提高数据读取效率？？
                                    {
                                        postProcessed.chart1.Series[0].Points.AddY(postData[i]); //x
                                        postProcessed.chart1.Series[1].Points.AddY(postData[i+1]); //y
                                        postProcessed.chart1.Series[2].Points.AddY(postData[i+2]);
                                        i += 3;
                                        //postData.RemoveRange(0, 9);//不要每次都移出，最后一起删除，防止过分浪费时间
                                    }
                                }
                            )
                    );
                    postData.Clear();   //清空所有数据
                }
            }
            catch (DataException ex)
            {
                MessageBox.Show(ex.ToString());
            }
            finally
            {
                connection.Close();
            }
        }

        private void deletebtn_Click(object sender, EventArgs e)
        {
            DialogResult results;
            results = MessageBox.Show("删除后历史数据不存在，确认删除？", "系统提醒！", MessageBoxButtons.YesNoCancel,
                MessageBoxIcon.Warning);
            if (results == DialogResult.Yes)
            {
                string conStr = "Data Source=.;Initial Catalog=用户登记;Integrated Security=True";
                SqlConnection connection = new SqlConnection(conStr);
                string sqlcmmd = "DELETE FROM 惯导数据";
                try
                {
                    connection.Open();
                    SqlCommand cmd = new SqlCommand(sqlcmmd, connection);
                    cmd.ExecuteNonQuery();     //删除所有历史数据
                }
                catch (DataException ex)
                {
                    MessageBox.Show(ex.ToString());
                }
                finally
                {
                    connection.Close();
                }
            }
        }

        //private void bgncalibratebtn_Click(object sender, EventArgs e)
        //{

        //}
    }
}
